/* 
 * Copyright: Copyright 2010 Topic Maps Lab, University of Leipzig. http://www.topicmapslab.de/    
 * License:   Apache License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0.html
 * 
 * @author Sven Krosse
 * @email krosse@informatik.uni-leipzig.de
 *
 */
package de.topicmapslab.ctm.writer.properties;

import java.io.IOException;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;

/**
 * Properties of the CTM topic map writer. All possible property-keys are static
 * constants of this class.
 * 
 * @author Sven Krosse
 * @email krosse@informatik.uni-leipzig.de
 * 
 */
public class CTMTopicMapWriterProperties extends Properties {

	private static final long serialVersionUID = 1L;

	/**
	 * CTMTopicMapWriter Property String for detecting engine prefixes as part
	 * of internal topic maps engine identifier. Value should be a string
	 * without containing a colon.
	 */
	public static final String IDENTITY_ENGINEPREFIX = "writer.identity.engineprefix";

	/**
	 * CTMTopicMapWriter Feature String for enabling the export of
	 * item-identifier. Value should be a string containing <code>true</code> or
	 * <code>false</code>.
	 */
	public static final String FEATURE_EXPORT_ITEMIDENTIFIER = "writer.features.export.itemidentifier";

	/**
	 * CTMTopicMapWriter Feature String for enabling the auto-prefix-detection.
	 * Value should be a string containing <code>true</code> or
	 * <code>false</code>.
	 */
	public static final String FEATURE_PREFIXDETECTION_ENABLED = "writer.features.prefixDetection.enabled";
	/**
	 * CTMTopicMapWriter Feature String for enabling the
	 * auto-template-detection. Value should be a string containing
	 * <code>true</code> or <code>false</code>.
	 */
	public static final String FEATURE_TEMPLATEDETECTION_ENABLED = "writer.features.templateDetection.enabled";
	/**
	 * CTMTopicMapWriter Feature String for enabling the auto-template-detection
	 * for topic templates. Value should be a string containing
	 * <code>true</code> or <code>false</code>.
	 */
	public static final String FEATURE_TEMPLATEDETECTION_TOPICTEMPLATES = "writer.features.templateDetection.topicTemplates";
	/**
	 * CTMTopicMapWriter Feature String for enabling the auto-template-detection
	 * for association templates. Value should be a string containing
	 * <code>true</code> or <code>false</code>.
	 */
	public static final String FEATURE_TEMPLATEDETECTION_ASSOCIATIONTEMPLATES = "writer.features.templateDetection.associationTemplates";
	/**
	 * CTMTopicMapWriter Feature String for the relevance threshold used to
	 * choose relevant candidates for template detection. Value should be a
	 * string containing a floating-point number between 0 and 1.
	 */
	public static final String FEATURE_TEMPLATEDETECTION_RELEVANCETHRESHOLD = "writer.features.templateDetection.relevanceThreshold";

	/**
	 * CTMTopicMapWriter Feature String for enabling the auto-template-merger.
	 * This process try to detect template patterns and extract them as
	 * template-invocations. Value should be a string containing
	 * <code>true</code> or <code>false</code>.
	 */
	public static final String FEATURE_TEMPLATEMERGER_ENABLED = "writer.features.templateMerger.enabled";

	/**
	 * CTMTopicMapWriter Feature String for the threshold used to choose
	 * relevant candidates for template merging. Value should be a string
	 * containing a decimal number greater than 1.
	 */
	public static final String FEATURE_TEMPLATEMERGER_THRESHOLD = "writer.features.templateMerger.threshold";

	/**
	 * CTMTopicMapWriter Feature String for enabling the automatic export of
	 * defined templates. If the value is <code>true</code> all templates will
	 * be exported, except the restricted one specified by
	 * {@link #getRestrictedTemplatesToExport()}. If the value is
	 * <code>false</code> no template definition will be exported.
	 */
	public static final String FEATURE_TEMPLATEEXPORT_ENABLED = "writer.features.templateExport.enabled";

	/**
	 * a set containing all names of template, which should never exported.
	 */
	private final Set<String> restrictedTemplatesToExport = new HashSet<String>();

	/**
	 * Constructor to load the property-file CTM-writer.properties
	 */
	public CTMTopicMapWriterProperties() {
		try {
			load(getClass().getResourceAsStream("ctm-writer.properties"));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Method return the prefix used to identify item-identifier generated by
	 * the topic map engine itself.
	 * 
	 * @return the prefix of the topic map engine
	 */
	public String getEnginePrefix() {
		return getProperty(IDENTITY_ENGINEPREFIX);
	}

	/**
	 * Setter of the prefix used to identify item-identifier generated by the
	 * topic map engine itself.
	 * 
	 * @param identityEnginePrefix
	 *            the new prefix
	 */
	public void setEnginePrefix(final String identityEnginePrefix) {
		setProperty(IDENTITY_ENGINEPREFIX, identityEnginePrefix);
	}

	/**
	 * Check if the export of item-identifier is enabled.
	 * 
	 * @return <code>true</code> if the auto-detection is enabled,
	 *         <code>false</code> otherwise.
	 */
	public boolean isExportOfItemIdentifierEnabled() {
		return Boolean.parseBoolean(getProperty(FEATURE_EXPORT_ITEMIDENTIFIER));
	}

	/**
	 * Enables or disables the export of item-identifier.
	 * 
	 * @param enable
	 *            the new state
	 */
	public void enableExportOfItemIdentifier(final boolean enable) {
		setProperty(FEATURE_EXPORT_ITEMIDENTIFIER, Boolean.toString(enable));
	}

	/**
	 * Check if the auto-detection of prefixes is enabled.
	 * 
	 * @return <code>true</code> if the auto-detection is enabled,
	 *         <code>false</code> otherwise.
	 */
	public boolean isPrefixDetectionEnabled() {
		return Boolean
				.parseBoolean(getProperty(FEATURE_PREFIXDETECTION_ENABLED));
	}

	/**
	 * Enables or disables the auto-detection of prefixes.
	 * 
	 * @param enable
	 *            the new state
	 */
	public void enablePrefixDetection(final boolean enable) {
		setProperty(FEATURE_PREFIXDETECTION_ENABLED, Boolean.toString(enable));
	}

	/**
	 * Check if the auto-detection of prefixes is enabled.
	 * 
	 * @return <code>true</code> if the auto-detection is enabled,
	 *         <code>false</code> otherwise.
	 */
	public boolean isTemplateDetectionEnabled() {
		return Boolean
				.parseBoolean(getProperty(FEATURE_TEMPLATEDETECTION_ENABLED));
	}

	/**
	 * Enables or disables the auto-detection of prefixes.
	 * 
	 * @param enable
	 *            the new state
	 */
	public void enableTemplateDetection(final boolean enable) {
		setProperty(FEATURE_TEMPLATEDETECTION_ENABLED, Boolean.toString(enable));
	}

	/**
	 * Check if the auto-detection of templates is enabled for topic-templates.
	 * 
	 * @return <code>true</code> if the auto-detection is enabled,
	 *         <code>false</code> otherwise.
	 */
	public boolean isTopicTemplateDetectionSupported() {
		return Boolean
				.parseBoolean(getProperty(FEATURE_TEMPLATEDETECTION_TOPICTEMPLATES));
	}

	/**
	 * Enables or disables the auto-detection of templates of topic templates.
	 * 
	 * @param enable
	 *            the new state
	 */
	public void enableTopicTemplateDetection(final boolean enable) {
		setProperty(FEATURE_TEMPLATEDETECTION_TOPICTEMPLATES, Boolean
				.toString(enable));
	}

	/**
	 * Check if the auto-detection of templates is enabled for
	 * association-templates.
	 * 
	 * @return <code>true</code> if the auto-detection is enabled,
	 *         <code>false</code> otherwise.
	 */
	public boolean isAssociationTemplateDetectionSupported() {
		return Boolean
				.parseBoolean(getProperty(FEATURE_TEMPLATEDETECTION_ASSOCIATIONTEMPLATES));
	}

	/**
	 * Enables or disables the auto-detection of templates for
	 * association-templates.
	 * 
	 * @param enable
	 *            the new state
	 */
	public void enableAssociationTemplateDetection(final boolean enable) {
		setProperty(FEATURE_TEMPLATEDETECTION_ASSOCIATIONTEMPLATES, Boolean
				.toString(enable));
	}

	/**
	 * Return the relevance threshold for auto-detection of template. The
	 * threshold define the minimum frequency of possible use of a detected
	 * template.
	 * 
	 * @return the relevance threshold
	 */
	public float getTemplateDetectionRelevanceThreshold() {
		return Float
				.parseFloat(getProperty(FEATURE_TEMPLATEDETECTION_RELEVANCETHRESHOLD));
	}

	/**
	 * Setter of the relevance threshold for auto-detection of template. The
	 * threshold define the minimum frequency of possible use of a detected
	 * template.
	 * 
	 * @param templateDetectionRelevanceThreshold
	 *            the new value for relevance threshold
	 */
	public void setTemplateDetectionRelevanceThreshold(
			float templateDetectionRelevanceThreshold) {
		setProperty(FEATURE_TEMPLATEDETECTION_RELEVANCETHRESHOLD, Float
				.toString(templateDetectionRelevanceThreshold));
	}

	/**
	 * Check if the auto-merging of templates is enabled.
	 * 
	 * @return <code>true</code> if the auto-merging is enabled,
	 *         <code>false</code> otherwise.
	 */
	public boolean isTemplateMergerEnabled() {
		return Boolean
				.parseBoolean(getProperty(FEATURE_TEMPLATEMERGER_ENABLED));
	}

	/**
	 * Enables or disables the auto-merging of templates.
	 * 
	 * @param enable
	 *            the new state
	 */
	public void enableTemplateMerger(final boolean enable) {
		setProperty(FEATURE_TEMPLATEMERGER_ENABLED, Boolean.toString(enable));
	}

	/**
	 * Returns the threshold as a minimum value for the number of entries
	 * contained in the merged template.
	 * 
	 * @param templateMergerThreshold
	 *            the threshold
	 */
	public long getTemplateMergerThreshold() {
		return Long.parseLong(getProperty(FEATURE_TEMPLATEMERGER_THRESHOLD));
	}

	/**
	 * Setter of the threshold as a minimum value for the number of entries
	 * contained in the merged template.
	 * 
	 * @return the threshold
	 */
	public void setTemplateMergerThreshold(long templateMergerThreshold) {
		setProperty(FEATURE_TEMPLATEMERGER_THRESHOLD, Long
				.toString(templateMergerThreshold));
	}

	/**
	 * Check if the export of template definition is enabled.
	 * 
	 * @return <code>true</code> if the export is enabled, <code>false</code>
	 *         otherwise.
	 */
	public boolean isTemplateExportEnabled() {
		return Boolean
				.parseBoolean(getProperty(FEATURE_TEMPLATEEXPORT_ENABLED));
	}

	/**
	 * Enables or disables the export of all template definitions.
	 * 
	 * @param enable
	 *            the new state
	 */
	public void enableTemplateExport(final boolean enable) {
		setProperty(FEATURE_TEMPLATEEXPORT_ENABLED, Boolean.toString(enable));
	}

	/**
	 * Checks the plausibility of values before setting it.
	 * 
	 * {@inheritDoc}
	 */
	@Override
	public synchronized Object setProperty(String key, String value) {
		if (key.equalsIgnoreCase(FEATURE_TEMPLATEDETECTION_RELEVANCETHRESHOLD)) {
			try {
				float v = Float.parseFloat(value);
				if (v < 0 || v > 1) {
					throw new IllegalArgumentException(
							"Value has to be a floating-point number between 0 and 1.");
				}
			} catch (NumberFormatException e) {
				throw new IllegalArgumentException(
						"Value has to be a floating-point number between 0 and 1.");
			}
		} else if (key.equalsIgnoreCase(FEATURE_TEMPLATEMERGER_THRESHOLD)) {
			try {
				long v = Long.parseLong(value);
				if (v < 0 || v > 1) {
					throw new IllegalArgumentException(
							"Value has to be a decimal number greater than 1.");
				}
			} catch (NumberFormatException e) {
				throw new IllegalArgumentException(
						"Value has to be a decimal number greater than 1.");
			}
		}
		return super.setProperty(key, value);

	}

	/**
	 * Method is called to set properties as property line. The property line
	 * will be parsed and the key-value-pairs will be extracted before setting
	 * the properties to property map. The key-value-pairs have to be separated
	 * with a comma. The key-value-pair has to match the typical pattern for
	 * java properties " key = value ", others will be ignored.
	 * 
	 * @param propertyLine
	 *            the property line to parse
	 */
	public void parse(final String propertyLine) {
		StringTokenizer properties = new StringTokenizer(propertyLine, ",");
		while (properties.hasMoreTokens()) {
			StringTokenizer tokenizer = new StringTokenizer(properties
					.nextToken(), "=");
			if (tokenizer.countTokens() == 2) {
				String key = tokenizer.nextToken().trim();
				String value = tokenizer.nextToken().trim();
				setProperty(key, value);
			}
		}
	}

	/**
	 * Add a new template name to restriction set. Each template which name is
	 * contained by this restriction list, will never been exported.
	 * 
	 * @param name
	 *            the name of the restricted template
	 */
	public void addRestrictionForTemplateExport(final String name) {
		this.restrictedTemplatesToExport.add(name);
	}

	/**
	 * Returns all names of all templates, which should never exported.
	 * 
	 * @return a set containing all template names
	 */
	public Set<String> getRestrictedTemplatesToExport() {
		return restrictedTemplatesToExport;
	}
}
